// 数据可视化组件库
// 提供各类图表和数据展示组件，支持Canvas绘制和响应式设计

// 图表类型常量
export const CHART_TYPES = {
  LINE: 'line',
  BAR: 'bar',
  PIE: 'pie',
  RADAR: 'radar',
  PROGRESS: 'progress',
  HEATMAP: 'heatmap',
  GAUGE: 'gauge',
  SCATTER: 'scatter',
  DONUT: 'donut',
  BUBBLE: 'bubble'
};

/**
 * 基础图表类
 * 所有具体图表组件的基类，提供通用功能
 */
class BaseChart {
  /**
   * 构造函数
   * @param {Object} options - 图表配置选项
   * @param {string} options.canvasId - Canvas元素ID
   * @param {number} options.width - 图表宽度
   * @param {number} options.height - 图表高度
   * @param {Object} options.theme - 主题配置
   */
  constructor(options = {}) {
    this.canvasId = options.canvasId || 'chart-canvas';
    this.width = options.width || 300;
    this.height = options.height || 200;
    this.theme = {
      colors: ['#2196F3', '#4CAF50', '#FF9800', '#F44336', '#9C27B0', '#3F51B5', '#00BCD4', '#FFC107'],
      background: '#ffffff',
      textColor: '#333333',
      gridColor: '#e0e0e0',
      ...options.theme
    };
    this.data = [];
    this.ctx = null;
    this.isReady = false;
    
    // 初始化图表
    this.init();
  }
  
  /**
   * 初始化图表
   */
  async init() {
    try {
      // 获取绘图上下文
      const query = uni.createSelectorQuery();
      query.select(`#${this.canvasId}`)
        .fields({
          node: true,
          size: true
        })
        .exec((res) => {
          if (res && res[0] && res[0].node) {
            const canvas = res[0].node;
            const ctx = canvas.getContext('2d');
            
            // 设置Canvas尺寸
            canvas.width = this.width;
            canvas.height = this.height;
            
            this.ctx = ctx;
            this.isReady = true;
            
            // 触发初始化完成事件
            this.onReady();
            
            // 如果已有数据，立即绘制
            if (this.data && this.data.length > 0) {
              this.render();
            }
          }
        });
    } catch (error) {
      console.error('图表初始化失败:', error);
    }
  }
  
  /**
   * 设置数据
   * @param {Array|Object} data - 图表数据
   * @returns {BaseChart} 当前实例，支持链式调用
   */
  setData(data) {
    this.data = data;
    if (this.isReady) {
      this.render();
    }
    return this;
  }
  
  /**
   * 更新配置
   * @param {Object} options - 配置选项
   * @returns {BaseChart} 当前实例，支持链式调用
   */
  updateOptions(options) {
    if (options.width) this.width = options.width;
    if (options.height) this.height = options.height;
    if (options.theme) {
      this.theme = { ...this.theme, ...options.theme };
    }
    
    if (this.isReady) {
      // 重新设置Canvas尺寸
      if (options.width || options.height) {
        const canvas = this.ctx.canvas;
        if (canvas) {
          canvas.width = this.width;
          canvas.height = this.height;
        }
      }
      this.render();
    }
    
    return this;
  }
  
  /**
   * 渲染图表
   * 子类需要实现此方法
   */
  render() {
    throw new Error('子类必须实现render方法');
  }
  
  /**
   * 初始化完成回调
   * 子类可以覆盖此方法
   */
  onReady() {
    console.log(`图表 ${this.canvasId} 初始化完成`);
  }
  
  /**
   * 清空画布
   */
  clear() {
    if (this.ctx) {
      this.ctx.clearRect(0, 0, this.width, this.height);
    }
  }
  
  /**
   * 保存图表为图片
   * @returns {Promise} 返回图片临时路径
   */
  async saveAsImage() {
    return new Promise((resolve, reject) => {
      try {
        uni.canvasToTempFilePath({
          canvasId: this.canvasId,
          success: (res) => {
            resolve(res.tempFilePath);
          },
          fail: (err) => {
            reject(err);
          }
        });
      } catch (error) {
        reject(error);
      }
    });
  }
  
  /**
   * 工具方法：绘制文本
   * @param {string} text - 文本内容
   * @param {number} x - X坐标
   * @param {number} y - Y坐标
   * @param {Object} options - 文本样式选项
   */
  drawText(text, x, y, options = {}) {
    if (!this.ctx) return;
    
    const {
      fontSize = 12,
      color = this.theme.textColor,
      align = 'left',
      baseline = 'top',
      font = 'Arial'
    } = options;
    
    this.ctx.save();
    this.ctx.font = `${fontSize}px ${font}`;
    this.ctx.fillStyle = color;
    this.ctx.textAlign = align;
    this.ctx.textBaseline = baseline;
    this.ctx.fillText(text, x, y);
    this.ctx.restore();
  }
  
  /**
   * 工具方法：绘制网格
   * @param {Object} options - 网格选项
   */
  drawGrid(options = {}) {
    if (!this.ctx) return;
    
    const {
      rows = 5,
      cols = 5,
      padding = { top: 20, right: 20, bottom: 20, left: 20 }
    } = options;
    
    const gridWidth = this.width - padding.left - padding.right;
    const gridHeight = this.height - padding.top - padding.bottom;
    const cellWidth = gridWidth / cols;
    const cellHeight = gridHeight / rows;
    
    this.ctx.save();
    this.ctx.strokeStyle = this.theme.gridColor;
    this.ctx.lineWidth = 0.5;
    
    // 绘制垂直线
    for (let i = 0; i <= cols; i++) {
      const x = padding.left + i * cellWidth;
      this.ctx.beginPath();
      this.ctx.moveTo(x, padding.top);
      this.ctx.lineTo(x, this.height - padding.bottom);
      this.ctx.stroke();
    }
    
    // 绘制水平线
    for (let i = 0; i <= rows; i++) {
      const y = padding.top + i * cellHeight;
      this.ctx.beginPath();
      this.ctx.moveTo(padding.left, y);
      this.ctx.lineTo(this.width - padding.right, y);
      this.ctx.stroke();
    }
    
    this.ctx.restore();
  }
}

/**
 * 折线图组件
 */
class LineChart extends BaseChart {
  constructor(options) {
    super(options);
    this.config = {
      showPoints: true,
      showLines: true,
      showArea: false,
      pointRadius: 4,
      smooth: true,
      ...options.config
    };
  }
  
  render() {
    if (!this.ctx || !this.data || !this.data.length) return;
    
    this.clear();
    
    // 设置内边距
    const padding = { top: 20, right: 30, bottom: 30, left: 40 };
    const chartWidth = this.width - padding.left - padding.right;
    const chartHeight = this.height - padding.top - padding.bottom;
    
    // 计算数据范围
    const values = this.data.map(item => item.value);
    const minValue = Math.min(...values);
    const maxValue = Math.max(...values);
    const valueRange = maxValue - minValue || 1;
    
    // 绘制网格
    this.drawGrid({ rows: 5, cols: this.data.length - 1, padding });
    
    // 绘制数据线
    if (this.config.showLines || this.config.showArea) {
      this.ctx.save();
      
      // 移动到第一个点
      let firstPoint = true;
      const points = [];
      
      this.data.forEach((item, index) => {
        const x = padding.left + (index / (this.data.length - 1)) * chartWidth;
        const y = this.height - padding.bottom - ((item.value - minValue) / valueRange) * chartHeight;
        points.push({ x, y, value: item.value });
        
        if (firstPoint) {
          this.ctx.beginPath();
          this.ctx.moveTo(x, y);
          firstPoint = false;
        } else if (this.config.smooth) {
          // 平滑曲线
          const prevPoint = points[index - 1];
          const cpx = (prevPoint.x + x) / 2;
          this.ctx.quadraticCurveTo(prevPoint.x, prevPoint.y, cpx, (prevPoint.y + y) / 2);
        } else {
          // 直线
          this.ctx.lineTo(x, y);
        }
      });
      
      // 如果是平滑曲线，处理最后一个点
      if (this.config.smooth && points.length > 1) {
        const lastPoint = points[points.length - 1];
        const prevPoint = points[points.length - 2];
        const cpx = (prevPoint.x + lastPoint.x) / 2;
        this.ctx.quadraticCurveTo(prevPoint.x, prevPoint.y, cpx, (prevPoint.y + lastPoint.y) / 2);
        this.ctx.lineTo(lastPoint.x, lastPoint.y);
      }
      
      if (this.config.showArea) {
        // 绘制填充区域
        this.ctx.lineTo(this.width - padding.right, this.height - padding.bottom);
        this.ctx.lineTo(padding.left, this.height - padding.bottom);
        this.ctx.closePath();
        this.ctx.fillStyle = `${this.theme.colors[0]}20`; // 20% 透明度
        this.ctx.fill();
      }
      
      if (this.config.showLines) {
        // 绘制线条
        this.ctx.strokeStyle = this.theme.colors[0];
        this.ctx.lineWidth = 2;
        this.ctx.stroke();
      }
      
      this.ctx.restore();
    }
    
    // 绘制数据点
    if (this.config.showPoints) {
      this.data.forEach((item, index) => {
        const x = padding.left + (index / (this.data.length - 1)) * chartWidth;
        const y = this.height - padding.bottom - ((item.value - minValue) / valueRange) * chartHeight;
        
        this.ctx.save();
        this.ctx.beginPath();
        this.ctx.arc(x, y, this.config.pointRadius, 0, Math.PI * 2);
        this.ctx.fillStyle = this.theme.colors[0];
        this.ctx.fill();
        
        // 绘制点边框
        this.ctx.strokeStyle = this.theme.background;
        this.ctx.lineWidth = 1;
        this.ctx.stroke();
        this.ctx.restore();
      });
    }
    
    // 绘制坐标轴标签
    // X轴标签
    this.data.forEach((item, index) => {
      const x = padding.left + (index / (this.data.length - 1)) * chartWidth;
      const label = item.label || `数据${index + 1}`;
      this.drawText(label, x, this.height - padding.bottom / 2, {
        align: 'center',
        fontSize: 10,
        color: this.theme.textColor
      });
    });
    
    // Y轴标签（最大值、中间值、最小值）
    const yLabels = [maxValue, (maxValue + minValue) / 2, minValue];
    yLabels.forEach((label, index) => {
      const y = padding.top + (index / 2) * chartHeight;
      this.drawText(label.toFixed(0), padding.left / 2, y, {
        align: 'center',
        baseline: 'middle',
        fontSize: 10,
        color: this.theme.textColor
      });
    });
  }
}

/**
 * 柱状图组件
 */
class BarChart extends BaseChart {
  constructor(options) {
    super(options);
    this.config = {
      barWidth: '60%',
      showValue: false,
      ...options.config
    };
  }
  
  render() {
    if (!this.ctx || !this.data || !this.data.length) return;
    
    this.clear();
    
    // 设置内边距
    const padding = { top: 20, right: 20, bottom: 40, left: 40 };
    const chartWidth = this.width - padding.left - padding.right;
    const chartHeight = this.height - padding.top - padding.bottom;
    
    // 计算数据范围
    const values = this.data.map(item => item.value);
    const maxValue = Math.max(...values);
    const valueRange = maxValue || 1;
    
    // 计算柱子宽度和间距
    const totalBarWidth = chartWidth / this.data.length;
    const barWidth = totalBarWidth * (parseFloat(this.config.barWidth) / 100);
    const barGap = (totalBarWidth - barWidth) / 2;
    
    // 绘制网格线（水平）
    this.ctx.save();
    this.ctx.strokeStyle = this.theme.gridColor;
    this.ctx.lineWidth = 0.5;
    
    // 绘制5条水平网格线
    for (let i = 0; i <= 4; i++) {
      const y = padding.top + (i / 4) * chartHeight;
      this.ctx.beginPath();
      this.ctx.moveTo(padding.left, y);
      this.ctx.lineTo(this.width - padding.right, y);
      this.ctx.stroke();
    }
    this.ctx.restore();
    
    // 绘制柱子
    this.data.forEach((item, index) => {
      const x = padding.left + index * totalBarWidth + barGap;
      const barHeight = (item.value / valueRange) * chartHeight;
      const y = this.height - padding.bottom - barHeight;
      
      // 选择颜色
      const color = this.theme.colors[index % this.theme.colors.length];
      
      // 绘制柱子
      this.ctx.save();
      this.ctx.fillStyle = color;
      this.ctx.fillRect(x, y, barWidth, barHeight);
      
      // 绘制柱子边框
      this.ctx.strokeStyle = `${color}80`;
      this.ctx.lineWidth = 1;
      this.ctx.strokeRect(x, y, barWidth, barHeight);
      this.ctx.restore();
      
      // 显示数值
      if (this.config.showValue) {
        this.drawText(item.value.toString(), x + barWidth / 2, y - 10, {
          align: 'center',
          fontSize: 10,
          color: this.theme.textColor
        });
      }
    });
    
    // 绘制坐标轴标签
    // X轴标签
    this.data.forEach((item, index) => {
      const x = padding.left + index * totalBarWidth + totalBarWidth / 2;
      const label = item.label || `类别${index + 1}`;
      
      // 处理长标签，换行显示
      const labelParts = this.wrapText(label, totalBarWidth - 10, 10);
      labelParts.forEach((part, partIndex) => {
        this.drawText(part, x, this.height - padding.bottom / 2 + partIndex * 12, {
          align: 'center',
          fontSize: 10,
          color: this.theme.textColor
        });
      });
    });
    
    // Y轴标签（最大值、中间值、最小值）
    const yLabels = [maxValue, maxValue / 2, 0];
    yLabels.forEach((label, index) => {
      const y = padding.top + (index / 2) * chartHeight;
      this.drawText(label.toFixed(0), padding.left / 2, y, {
        align: 'center',
        baseline: 'middle',
        fontSize: 10,
        color: this.theme.textColor
      });
    });
  }
  
  /**
   * 辅助方法：文本换行
   * @param {string} text - 文本内容
   * @param {number} maxWidth - 最大宽度
   * @param {number} fontSize - 字体大小
   * @returns {Array} 换行后的文本数组
   */
  wrapText(text, maxWidth, fontSize) {
    const result = [];
    const words = text.split(' ');
    let currentLine = words[0];
    
    for (let i = 1; i < words.length; i++) {
      const testLine = `${currentLine} ${words[i]}`;
      const metrics = this.ctx.measureText(testLine);
      
      if (metrics.width > maxWidth) {
        result.push(currentLine);
        currentLine = words[i];
      } else {
        currentLine = testLine;
      }
    }
    
    result.push(currentLine);
    return result;
  }
}

/**
 * 饼图组件
 */
class PieChart extends BaseChart {
  constructor(options) {
    super(options);
    this.config = {
      showLabels: true,
      showValues: false,
      radius: '70%',
      innerRadius: 0, // 0表示饼图，大于0表示环形图
      labelPosition: 'outside', // inside, outside, none
      ...options.config
    };
  }
  
  render() {
    if (!this.ctx || !this.data || !this.data.length) return;
    
    this.clear();
    
    // 计算圆心和半径
    const centerX = this.width / 2;
    const centerY = this.height / 2;
    const radius = Math.min(centerX, centerY) * (parseFloat(this.config.radius) / 100);
    const innerRadius = this.config.innerRadius || 0;
    
    // 计算数据总和
    const totalValue = this.data.reduce((sum, item) => sum + item.value, 0);
    
    // 绘制饼图扇区
    let currentAngle = -Math.PI / 2; // 从顶部开始
    const labelPositions = [];
    
    this.data.forEach((item, index) => {
      // 计算当前扇区的角度
      const sliceAngle = (item.value / totalValue) * Math.PI * 2;
      const nextAngle = currentAngle + sliceAngle;
      
      // 选择颜色
      const color = this.theme.colors[index % this.theme.colors.length];
      
      // 绘制扇区
      this.ctx.save();
      this.ctx.beginPath();
      
      // 如果是环形图
      if (innerRadius > 0) {
        this.ctx.moveTo(centerX, centerY);
        this.ctx.arc(centerX, centerY, radius, currentAngle, nextAngle);
        this.ctx.lineTo(centerX, centerY);
        this.ctx.moveTo(centerX, centerY);
        this.ctx.arc(centerX, centerY, innerRadius, nextAngle, currentAngle, true);
        this.ctx.lineTo(centerX, centerY);
      } else {
        this.ctx.moveTo(centerX, centerY);
        this.ctx.arc(centerX, centerY, radius, currentAngle, nextAngle);
        this.ctx.closePath();
      }
      
      this.ctx.fillStyle = color;
      this.ctx.fill();
      
      // 绘制扇区边框
      this.ctx.strokeStyle = this.theme.background;
      this.ctx.lineWidth = 2;
      this.ctx.stroke();
      this.ctx.restore();
      
      // 计算标签位置（扇区中心）
      const labelAngle = currentAngle + sliceAngle / 2;
      const labelRadius = radius + (this.config.labelPosition === 'outside' ? 30 : 0);
      
      labelPositions.push({
        x: centerX + Math.cos(labelAngle) * labelRadius,
        y: centerY + Math.sin(labelAngle) * labelRadius,
        label: item.label || `类别${index + 1}`,
        value: item.value,
        color: color,
        percentage: (item.value / totalValue * 100).toFixed(1),
        angle: labelAngle
      });
      
      // 如果是内部标签，直接绘制
      if (this.config.labelPosition === 'inside' && sliceAngle > 0.3) { // 只在扇区足够大时显示内部标签
        const innerLabelRadius = (radius + innerRadius) / 2;
        const innerLabelX = centerX + Math.cos(labelAngle) * innerLabelRadius;
        const innerLabelY = centerY + Math.sin(labelAngle) * innerLabelRadius;
        
        this.drawText(`${labelPositions[index].percentage}%`, innerLabelX, innerLabelY, {
          align: 'center',
          baseline: 'middle',
          fontSize: 12,
          color: this.theme.background
        });
      }
      
      currentAngle = nextAngle;
    });
    
    // 绘制外部标签
    if (this.config.labelPosition === 'outside' && this.config.showLabels) {
      labelPositions.forEach(pos => {
        this.ctx.save();
        
        // 绘制连接线
        const lineEndRadius = radius + 10;
        const lineEndX = centerX + Math.cos(pos.angle) * lineEndRadius;
        const lineEndY = centerY + Math.sin(pos.angle) * lineEndRadius;
        
        this.ctx.strokeStyle = pos.color;
        this.ctx.lineWidth = 1;
        this.ctx.beginPath();
        this.ctx.moveTo(lineEndX, lineEndY);
        this.ctx.lineTo(pos.x, pos.y);
        this.ctx.stroke();
        
        // 确定文本对齐方式
        let align = 'center';
        if (pos.x < centerX) align = 'right';
        else if (pos.x > centerX) align = 'left';
        
        // 绘制标签文本
        const labelText = this.config.showValues 
          ? `${pos.label} ${pos.percentage}%` 
          : pos.label;
        
        this.drawText(labelText, pos.x, pos.y - 5, {
          align: align,
          fontSize: 11,
          color: this.theme.textColor
        });
        
        if (this.config.showValues && !labelText.includes(pos.value)) {
          this.drawText(pos.value.toString(), pos.x, pos.y + 10, {
            align: align,
            fontSize: 10,
            color: pos.color
          });
        }
        
        this.ctx.restore();
      });
    }
  }
}

/**
 * 进度条组件
 */
class ProgressChart extends BaseChart {
  constructor(options) {
    super(options);
    this.config = {
      showValue: true,
      height: 20,
      borderRadius: 10,
      animation: true,
      animationDuration: 1000,
      ...options.config
    };
    
    this.animatedProgress = 0;
  }
  
  render() {
    if (!this.ctx || typeof this.data !== 'number') return;
    
    this.clear();
    
    // 确保进度值在0-1之间
    const progress = Math.max(0, Math.min(1, this.data));
    const progressHeight = this.config.height;
    const centerY = this.height / 2;
    
    // 背景条
    this.ctx.save();
    this.ctx.fillStyle = this.theme.gridColor;
    this.ctx.beginPath();
    this.drawRoundedRect(
      0,
      centerY - progressHeight / 2,
      this.width,
      progressHeight,
      this.config.borderRadius
    );
    this.ctx.fill();
    this.ctx.restore();
    
    // 进度条
    if (this.config.animation) {
      // 动画更新进度
      if (this.animatedProgress < progress) {
        this.animatedProgress += 0.01;
        if (this.animatedProgress > progress) this.animatedProgress = progress;
        
        // 继续动画
        requestAnimationFrame(() => this.render());
      } else if (this.animatedProgress > progress) {
        this.animatedProgress = progress;
      }
      
      var currentProgress = this.animatedProgress;
    } else {
      var currentProgress = progress;
    }
    
    // 绘制当前进度
    this.ctx.save();
    this.ctx.fillStyle = this.theme.colors[0];
    this.ctx.beginPath();
    this.drawRoundedRect(
      0,
      centerY - progressHeight / 2,
      this.width * currentProgress,
      progressHeight,
      this.config.borderRadius
    );
    this.ctx.fill();
    this.ctx.restore();
    
    // 显示进度值
    if (this.config.showValue) {
      const progressText = `${Math.round(currentProgress * 100)}%`;
      this.drawText(progressText, this.width / 2, centerY, {
        align: 'center',
        baseline: 'middle',
        fontSize: 12,
        color: currentProgress > 0.5 ? this.theme.background : this.theme.textColor
      });
    }
  }
  
  /**
   * 绘制圆角矩形
   */
  drawRoundedRect(x, y, width, height, radius) {
    this.ctx.moveTo(x + radius, y);
    this.ctx.lineTo(x + width - radius, y);
    this.ctx.arc(x + width - radius, y + radius, radius, -Math.PI / 2, 0);
    this.ctx.lineTo(x + width, y + height - radius);
    this.ctx.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2);
    this.ctx.lineTo(x + radius, y + height);
    this.ctx.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI);
    this.ctx.lineTo(x, y + radius);
    this.ctx.arc(x + radius, y + radius, radius, Math.PI, -Math.PI / 2);
    this.ctx.closePath();
  }
}

/**
 * 雷达图组件
 */
class RadarChart extends BaseChart {
  constructor(options) {
    super(options);
    this.config = {
      showGrid: true,
      showLabels: true,
      showPoints: true,
      showArea: false,
      smooth: false,
      ...options.config
    };
  }
  
  render() {
    if (!this.ctx || !this.data || !this.data.length) return;
    
    this.clear();
    
    const centerX = this.width / 2;
    const centerY = this.height / 2;
    const radius = Math.min(centerX, centerY) * 0.8;
    const angleStep = (Math.PI * 2) / this.data.length;
    
    // 绘制网格
    if (this.config.showGrid) {
      this.ctx.save();
      this.ctx.strokeStyle = this.theme.gridColor;
      this.ctx.lineWidth = 0.5;
      
      // 绘制同心圆网格
      for (let level = 1; level <= 5; level++) {
        const gridRadius = (radius / 5) * level;
        this.ctx.beginPath();
        this.ctx.arc(centerX, centerY, gridRadius, 0, Math.PI * 2);
        this.ctx.stroke();
      }
      
      // 绘制辐射线
      for (let i = 0; i < this.data.length; i++) {
        const angle = -Math.PI / 2 + i * angleStep;
        const x = centerX + Math.cos(angle) * radius;
        const y = centerY + Math.sin(angle) * radius;
        
        this.ctx.beginPath();
        this.ctx.moveTo(centerX, centerY);
        this.ctx.lineTo(x, y);
        this.ctx.stroke();
      }
      
      this.ctx.restore();
    }
    
    // 计算数据点坐标
    const points = this.data.map((item, index) => {
      const angle = -Math.PI / 2 + index * angleStep;
      const pointRadius = (item.value / 100) * radius;
      return {
        x: centerX + Math.cos(angle) * pointRadius,
        y: centerY + Math.sin(angle) * pointRadius,
        value: item.value
      };
    });
    
    // 绘制填充区域
    if (this.config.showArea && points.length > 2) {
      this.ctx.save();
      this.ctx.beginPath();
      this.ctx.moveTo(points[0].x, points[0].y);
      
      for (let i = 1; i < points.length; i++) {
        this.ctx.lineTo(points[i].x, points[i].y);
      }
      
      this.ctx.closePath();
      this.ctx.fillStyle = `${this.theme.colors[0]}20`; // 20% 透明度
      this.ctx.fill();
      this.ctx.restore();
    }
    
    // 绘制连接线
    this.ctx.save();
    this.ctx.beginPath();
    this.ctx.moveTo(points[0].x, points[0].y);
    
    if (this.config.smooth && points.length > 2) {
      // 平滑雷达图
      for (let i = 1; i < points.length; i++) {
        const nextIndex = (i + 1) % points.length;
        const xc = (points[i].x + points[nextIndex].x) / 2;
        const yc = (points[i].y + points[nextIndex].y) / 2;
        this.ctx.quadraticCurveTo(points[i].x, points[i].y, xc, yc);
      }
      
      // 闭合曲线
      const xc = (points[points.length - 1].x + points[0].x) / 2;
      const yc = (points[points.length - 1].y + points[0].y) / 2;
      this.ctx.quadraticCurveTo(
        points[points.length - 1].x, 
        points[points.length - 1].y, 
        xc, 
        yc
      );
    } else {
      // 直线连接
      for (let i = 1; i < points.length; i++) {
        this.ctx.lineTo(points[i].x, points[i].y);
      }
      this.ctx.closePath();
    }
    
    this.ctx.strokeStyle = this.theme.colors[0];
    this.ctx.lineWidth = 2;
    this.ctx.stroke();
    this.ctx.restore();
    
    // 绘制数据点
    if (this.config.showPoints) {
      points.forEach((point, index) => {
        this.ctx.save();
        this.ctx.beginPath();
        this.ctx.arc(point.x, point.y, 3, 0, Math.PI * 2);
        this.ctx.fillStyle = this.theme.colors[0];
        this.ctx.fill();
        
        this.ctx.strokeStyle = this.theme.background;
        this.ctx.lineWidth = 1;
        this.ctx.stroke();
        this.ctx.restore();
      });
    }
    
    // 绘制标签
    if (this.config.showLabels) {
      this.data.forEach((item, index) => {
        const angle = -Math.PI / 2 + index * angleStep;
        const labelRadius = radius + 20;
        const x = centerX + Math.cos(angle) * labelRadius;
        const y = centerY + Math.sin(angle) * labelRadius;
        
        // 确定文本对齐方式
        let align = 'center';
        let baseline = 'middle';
        
        if (Math.cos(angle) > 0.5) align = 'left';
        else if (Math.cos(angle) < -0.5) align = 'right';
        
        if (Math.sin(angle) > 0.5) baseline = 'top';
        else if (Math.sin(angle) < -0.5) baseline = 'bottom';
        
        this.drawText(item.label || `轴${index + 1}`, x, y, {
          align: align,
          baseline: baseline,
          fontSize: 11,
          color: this.theme.textColor
        });
      });
    }
  }
}

/**
 * 热力图组件
 */
class HeatmapChart extends BaseChart {
  constructor(options) {
    super(options);
    this.config = {
      cellSize: 20,
      cellGap: 2,
      showValue: false,
      colorRange: ['#E6F3FF', '#BAE7FF', '#91D5FF', '#69C0FF', '#40A9FF', '#1890FF', '#096DD9', '#0050B3'],
      ...options.config
    };
  }
  
  render() {
    if (!this.ctx || !this.data || !Array.isArray(this.data)) return;
    
    this.clear();
    
    const rows = this.data.length;
    const cols = this.data[0]?.length || 0;
    
    if (rows === 0 || cols === 0) return;
    
    const { cellSize, cellGap } = this.config;
    
    // 计算数据范围
    const allValues = this.data.flat();
    const minValue = Math.min(...allValues);
    const maxValue = Math.max(...allValues);
    const valueRange = maxValue - minValue || 1;
    
    // 计算热图位置以居中显示
    const heatmapWidth = cols * (cellSize + cellGap) - cellGap;
    const heatmapHeight = rows * (cellSize + cellGap) - cellGap;
    const offsetX = (this.width - heatmapWidth) / 2;
    const offsetY = (this.height - heatmapHeight) / 2;
    
    // 绘制热力图格子
    this.data.forEach((row, rowIndex) => {
      row.forEach((value, colIndex) => {
        // 计算格子位置
        const x = offsetX + colIndex * (cellSize + cellGap);
        const y = offsetY + rowIndex * (cellSize + cellGap);
        
        // 根据值计算颜色
        const normalizedValue = (value - minValue) / valueRange;
        const colorIndex = Math.floor(normalizedValue * (this.config.colorRange.length - 1));
        const color = this.config.colorRange[colorIndex];
        
        // 绘制格子
        this.ctx.save();
        this.ctx.fillStyle = color;
        this.ctx.fillRect(x, y, cellSize, cellSize);
        
        // 绘制格子边框
        this.ctx.strokeStyle = this.theme.background;
        this.ctx.lineWidth = 1;
        this.ctx.strokeRect(x, y, cellSize, cellSize);
        this.ctx.restore();
        
        // 显示数值
        if (this.config.showValue) {
          this.drawText(value.toString(), x + cellSize / 2, y + cellSize / 2, {
            align: 'center',
            baseline: 'middle',
            fontSize: Math.min(cellSize * 0.6, 12),
            color: normalizedValue > 0.5 ? this.theme.background : this.theme.textColor
          });
        }
      });
    });
  }
}

/**
 * 仪表盘组件
 */
class GaugeChart extends BaseChart {
  constructor(options) {
    super(options);
    this.config = {
      showValue: true,
      showLabel: true,
      minValue: 0,
      maxValue: 100,
      startAngle: 135,
      endAngle: 45,
      arcWidth: 20,
      animation: true,
      animationDuration: 1000,
      ...options.config
    };
    
    this.animatedValue = this.config.minValue;
  }
  
  render() {
    if (!this.ctx || typeof this.data !== 'number') return;
    
    this.clear();
    
    const centerX = this.width / 2;
    const centerY = this.height * 0.7;
    const radius = Math.min(centerX, centerY) * 0.8;
    
    // 转换角度为弧度
    const startAngle = (this.config.startAngle - 90) * Math.PI / 180;
    const endAngle = (this.config.endAngle - 90) * Math.PI / 180;
    const totalAngle = endAngle - startAngle;
    
    // 计算弧宽
    const arcWidth = this.config.arcWidth;
    const innerRadius = radius - arcWidth;
    
    // 绘制背景弧
    this.ctx.save();
    this.ctx.strokeStyle = this.theme.gridColor;
    this.ctx.lineWidth = arcWidth;
    this.ctx.lineCap = 'round';
    
    this.ctx.beginPath();
    this.ctx.arc(centerX, centerY, radius - arcWidth / 2, startAngle, endAngle);
    this.ctx.stroke();
    this.ctx.restore();
    
    // 计算进度值
    const minValue = this.config.minValue;
    const maxValue = this.config.maxValue;
    const value = Math.max(minValue, Math.min(maxValue, this.data));
    
    // 动画效果
    if (this.config.animation) {
      if (this.animatedValue < value) {
        this.animatedValue += (value - minValue) * 0.02;
        if (this.animatedValue > value) this.animatedValue = value;
        requestAnimationFrame(() => this.render());
      } else if (this.animatedValue > value) {
        this.animatedValue = value;
      }
      
      var currentValue = this.animatedValue;
    } else {
      var currentValue = value;
    }
    
    // 计算当前角度
    const progress = (currentValue - minValue) / (maxValue - minValue);
    const currentAngle = startAngle + totalAngle * progress;
    
    // 绘制进度弧
    this.ctx.save();
    this.ctx.strokeStyle = this.theme.colors[0];
    this.ctx.lineWidth = arcWidth;
    this.ctx.lineCap = 'round';
    
    this.ctx.beginPath();
    this.ctx.arc(centerX, centerY, radius - arcWidth / 2, startAngle, currentAngle);
    this.ctx.stroke();
    this.ctx.restore();
    
    // 显示值
    if (this.config.showValue) {
      this.drawText(currentValue.toFixed(1), centerX, centerY, {
        align: 'center',
        baseline: 'middle',
        fontSize: 24,
        color: this.theme.colors[0]
      });
    }
    
    // 显示标签
    if (this.config.showLabel) {
      const label = this.config.label || '';
      this.drawText(label, centerX, centerY + 30, {
        align: 'center',
        fontSize: 14,
        color: this.theme.textColor
      });
    }
    
    // 显示最小值和最大值
    const minValueX = centerX + Math.cos(startAngle) * (radius + 20);
    const minValueY = centerY + Math.sin(startAngle) * (radius + 20);
    const maxValueX = centerX + Math.cos(endAngle) * (radius + 20);
    const maxValueY = centerY + Math.sin(endAngle) * (radius + 20);
    
    this.drawText(minValue.toString(), minValueX, minValueY, {
      align: 'center',
      fontSize: 10,
      color: this.theme.textColor
    });
    
    this.drawText(maxValue.toString(), maxValueX, maxValueY, {
      align: 'center',
      fontSize: 10,
      color: this.theme.textColor
    });
  }
}

/**
 * 图表工厂类
 * 用于创建不同类型的图表实例
 */
class ChartFactory {
  /**
   * 创建图表实例
   * @param {string} type - 图表类型
   * @param {Object} options - 图表配置
   * @returns {BaseChart} 图表实例
   */
  static createChart(type, options) {
    switch (type) {
      case CHART_TYPES.LINE:
        return new LineChart(options);
      case CHART_TYPES.BAR:
        return new BarChart(options);
      case CHART_TYPES.PIE:
        return new PieChart(options);
      case CHART_TYPES.RADAR:
        return new RadarChart(options);
      case CHART_TYPES.PROGRESS:
        return new ProgressChart(options);
      case CHART_TYPES.HEATMAP:
        return new HeatmapChart(options);
      case CHART_TYPES.GAUGE:
        return new GaugeChart(options);
      case CHART_TYPES.DONUT:
        // 环形图是饼图的一种特殊形式
        return new PieChart({
          ...options,
          config: {
            ...options.config,
            innerRadius: (options.config?.radius || 70) * 0.5
          }
        });
      default:
        throw new Error(`不支持的图表类型: ${type}`);
    }
  }
}

/**
 * 数据可视化工具函数
 */
const ChartUtils = {
  /**
   * 格式化数字
   * @param {number} num - 数字
   * @param {number} decimals - 小数位数
   * @returns {string} 格式化后的数字
   */
  formatNumber(num, decimals = 0) {
    return num.toLocaleString('zh-CN', {
      minimumFractionDigits: decimals,
      maximumFractionDigits: decimals
    });
  },
  
  /**
   * 格式化百分比
   * @param {number} value - 数值
   * @param {number} decimals - 小数位数
   * @returns {string} 格式化后的百分比
   */
  formatPercentage(value, decimals = 1) {
    return `${(value * 100).toFixed(decimals)}%`;
  },
  
  /**
   * 格式化日期
   * @param {string|Date} date - 日期
   * @param {string} format - 格式
   * @returns {string} 格式化后的日期
   */
  formatDate(date, format = 'YYYY-MM-DD') {
    const d = new Date(date);
    const year = d.getFullYear();
    const month = String(d.getMonth() + 1).padStart(2, '0');
    const day = String(d.getDate()).padStart(2, '0');
    const hours = String(d.getHours()).padStart(2, '0');
    const minutes = String(d.getMinutes()).padStart(2, '0');
    const seconds = String(d.getSeconds()).padStart(2, '0');
    
    return format
      .replace('YYYY', year)
      .replace('MM', month)
      .replace('DD', day)
      .replace('HH', hours)
      .replace('mm', minutes)
      .replace('ss', seconds);
  },
  
  /**
   * 计算平均值
   * @param {Array} data - 数据数组
   * @returns {number} 平均值
   */
  calculateAverage(data) {
    if (!data || data.length === 0) return 0;
    const sum = data.reduce((acc, val) => acc + val, 0);
    return sum / data.length;
  },
  
  /**
   * 计算总和
   * @param {Array} data - 数据数组
   * @returns {number} 总和
   */
  calculateSum(data) {
    if (!data || data.length === 0) return 0;
    return data.reduce((acc, val) => acc + val, 0);
  },
  
  /**
   * 计算中位数
   * @param {Array} data - 数据数组
   * @returns {number} 中位数
   */
  calculateMedian(data) {
    if (!data || data.length === 0) return 0;
    const sorted = [...data].sort((a, b) => a - b);
    const mid = Math.floor(sorted.length / 2);
    return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
  },
  
  /**
   * 生成随机颜色
   * @param {number} count - 需要的颜色数量
   * @returns {Array} 颜色数组
   */
  generateColors(count) {
    const colors = [];
    const baseColors = [
      '#2196F3', '#4CAF50', '#FF9800', '#F44336', 
      '#9C27B0', '#3F51B5', '#00BCD4', '#FFC107'
    ];
    
    for (let i = 0; i < count; i++) {
      colors.push(baseColors[i % baseColors.length]);
    }
    
    return colors;
  },
  
  /**
   * 将数据分组
   * @param {Array} data - 原始数据
   * @param {Function} groupBy - 分组函数
   * @returns {Object} 分组后的数据
   */
  groupData(data, groupBy) {
    if (!data || !Array.isArray(data)) return {};
    
    return data.reduce((groups, item) => {
      const key = groupBy(item);
      if (!groups[key]) {
        groups[key] = [];
      }
      groups[key].push(item);
      return groups;
    }, {});
  },
  
  /**
   * 转换数据为图表格式
   * @param {Array} data - 原始数据
   * @param {string} xField - X轴字段
   * @param {string} yField - Y轴字段
   * @returns {Array} 图表格式数据
   */
  transformToChartData(data, xField, yField) {
    if (!data || !Array.isArray(data)) return [];
    
    return data.map(item => ({
      label: item[xField],
      value: item[yField]
    }));
  }
};

// 导出图表组件和工具
export {
  BaseChart,
  LineChart,
  BarChart,
  PieChart,
  RadarChart,
  ProgressChart,
  HeatmapChart,
  GaugeChart,
  ChartFactory,
  ChartUtils
};

export default {
  CHART_TYPES,
  BaseChart,
  LineChart,
  BarChart,
  PieChart,
  RadarChart,
  ProgressChart,
  HeatmapChart,
  GaugeChart,
  ChartFactory,
  ChartUtils
};